package com.hmall.search.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.hmall.api.client.ItemClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.common.utils.BeanUtils;
import com.hmall.search.domain.po.ItemDoc;
import com.hmall.search.domain.query.ItemPageQuery;
import com.hmall.search.domain.vo.PageVO;
import com.hmall.search.service.ISearchService;
import lombok.RequiredArgsConstructor;
import org.apache.http.HttpHost;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements ISearchService {
    //索引库名称
    private static final String INDEX_NAME = "items";

    @Autowired
    private ItemClient itemClient;

    private RestHighLevelClient client;

    public SearchServiceImpl() {
        client = new RestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.12.168:9200")));
    }

    @Override
    public void saveItemById(Long itemId) {
        try {
            //1、根据商品id查询商品信息
            ItemDTO itemDTO = itemClient.queryItemById(itemId);
            if (itemDTO != null) {
                //2、转换为itemDoc json字符串
                ItemDoc itemDoc = BeanUtils.copyBean(itemDTO, ItemDoc.class);
                String jsonStr = JSONUtil.toJsonStr(itemDoc);
                //3、创建新增请求
                IndexRequest request = new IndexRequest(INDEX_NAME).id(itemDoc.getId().toString());
                //4、设置source参数
                request.source(jsonStr, XContentType.JSON);
                //5、发送请求
                client.index(request, RequestOptions.DEFAULT);
            }
        } catch (IOException e) {
            throw new RuntimeException("更新es中商品失败！参数:" + itemId, e);
        }
    }

    @Override
    public void deleteItemById(Long itemId) {
        try {
            //1、创建删除请求
            DeleteRequest deleteRequest = new DeleteRequest(INDEX_NAME, itemId.toString());
            //2、发送请求
            client.delete(deleteRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException("删除es中商品失败！参数:" + itemId, e);
        }
    }

    @Override
    public PageVO<ItemDoc> search(ItemPageQuery query) {
//        创建请求
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);

//        设置请求对象的参数(搜索关键字、过滤、分页排序、高亮)
        boolean isHighlight = false;
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StrUtil.isNotBlank(query.getKey())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("name", query.getKey()));
            //需要进行关键字高亮
            isHighlight = true;

        }
//        根据分类条件分类过滤
        if (StrUtil.isNotBlank(query.getCategory())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category", query.getCategory()));
        }
        //        根据品牌过滤
        if (StrUtil.isNotBlank(query.getBrand())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand", query.getBrand()));
        }
//        根据价格区间过滤
        if (query.getMinPrice() != null) {

            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(query.getMinPrice()));
        }
        if (query.getMaxPrice() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(query.getMaxPrice()));
        }
//        设置排序
        if (StrUtil.isNotBlank(query.getSortBy())) {
            searchRequest.source().sort(query.getSortBy(), query.getIsAsc() ? SortOrder.ASC : SortOrder.DESC);
        } else {
//            默认按更新时间排序
            searchRequest.source().sort("updateTime", SortOrder.DESC);
        }

//        设置分页
        int pageNo = query.getPageNo() == null ? 1 : query.getPageNo();
        int pageSize = query.getPageSize() == null ? ItemPageQuery.DEFAULT_PAGE_SIZE : query.getPageSize();
        searchRequest.source().from((pageNo - 1) * pageSize).size(pageSize);
//        设置高亮
        if (isHighlight) {
            searchRequest.source().highlighter(SearchSourceBuilder.highlight().field("name").preTags("<span style='color:red'>").postTags("</span>"));
        }

        searchRequest.source().query(boolQueryBuilder);
//        发送请求
        try {
            //        处理响应结果
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

            PageVO<ItemDoc> pageVO = new PageVO<>();
            SearchHits searchHits = searchResponse.getHits();
            long total = searchHits.getTotalHits().value;
//            设置总记录数
            pageVO.setTotal(total);
            //            设置总页数
            pageVO.setPages(total / pageSize + 1);
            //           设置列表
            List<ItemDoc> itemDocList = new ArrayList<>(pageSize);
            SearchHit[] hits = searchHits.getHits();
            if (hits != null && hits.length > 0) {
                for (SearchHit hit : hits) {
                    String sourceAsString = hit.getSourceAsString();
                    ItemDoc itemDoc = JSONUtil.toBean(sourceAsString, ItemDoc.class);
//                    处理高亮
                    if (isHighlight) {
                        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                        if (highlightFields != null) {
                            HighlightField name = highlightFields.get("name");
                            if (name != null) {
                                itemDoc.setName(name.getFragments()[0].toString());
                            }
                        }
                    }
                    itemDocList.add(itemDoc);
                }
            }
            pageVO.setList(itemDocList);

            return pageVO;
        } catch (IOException e) {
            throw new RuntimeException("搜索es中的商品失败" + e.getMessage());
        }

    }
}
